using System;
using System.IO;

namespace RTMP.SimpleClient.Helpers
{
    /// <summary>
    /// 提供大端字节序与基本数据类型之间的转换方法（基于 Stream）
    /// </summary>
    public static class StreamBigEndianConverter
    {
        /// <summary>
        /// 从流中读取大端字节并转换为 16 位有符号整数
        /// </summary>
        /// <param name="stream">要读取的流</param>
        /// <returns>转换后的 short 值</returns>
        /// <exception cref="ArgumentException">当流不可读时抛出</exception>
        /// <exception cref="EndOfStreamException">当流中没有足够的字节时抛出</exception>
        public static short ReadBigEndianInt16(this Stream stream)
        {
            if (!stream.CanRead)
                throw new ArgumentException("流必须可读", nameof(stream));

            Span<byte> buffer = stackalloc byte[2];
            int bytesRead = stream.Read(buffer);
            if (bytesRead != 2)
                throw new EndOfStreamException("流中没有足够的字节来读取 short 值");

            return buffer.BigEndianToInt16();
        }

        /// <summary>
        /// 从流中读取大端字节并转换为 32 位有符号整数
        /// </summary>
        /// <param name="stream">要读取的流</param>
        /// <returns>转换后的 int 值</returns>
        /// <exception cref="ArgumentException">当流不可读时抛出</exception>
        /// <exception cref="EndOfStreamException">当流中没有足够的字节时抛出</exception>
        public static int ReadBigEndianInt32(this Stream stream)
        {
            if (!stream.CanRead)
                throw new ArgumentException("流必须可读", nameof(stream));

            Span<byte> buffer = stackalloc byte[4];
            int bytesRead = stream.Read(buffer);
            if (bytesRead != 4)
                throw new EndOfStreamException("流中没有足够的字节来读取 int 值");

            return buffer.BigEndianToInt32();
        }

        /// <summary>
        /// 从流中读取大端字节并转换为 64 位有符号整数
        /// </summary>
        /// <param name="stream">要读取的流</param>
        /// <returns>转换后的 long 值</returns>
        /// <exception cref="ArgumentException">当流不可读时抛出</exception>
        /// <exception cref="EndOfStreamException">当流中没有足够的字节时抛出</exception>
        public static long ReadBigEndianInt64(this Stream stream)
        {
            if (!stream.CanRead)
                throw new ArgumentException("流必须可读", nameof(stream));

            Span<byte> buffer = stackalloc byte[8];
            int bytesRead = stream.Read(buffer);
            if (bytesRead != 8)
                throw new EndOfStreamException("流中没有足够的字节来读取 long 值");

            return buffer.BigEndianToInt64();
        }

        /// <summary>
        /// 从流中读取大端字节并转换为双精度浮点数
        /// </summary>
        /// <param name="stream">要读取的流</param>
        /// <returns>转换后的 double 值</returns>
        /// <exception cref="ArgumentException">当流不可读时抛出</exception>
        /// <exception cref="EndOfStreamException">当流中没有足够的字节时抛出</exception>
        public static double ReadBigEndianDouble(this Stream stream)
        {
            if (!stream.CanRead)
                throw new ArgumentException("流必须可读", nameof(stream));

            Span<byte> buffer = stackalloc byte[8];
            int bytesRead = stream.Read(buffer);
            if (bytesRead != 8)
                throw new EndOfStreamException("流中没有足够的字节来读取 double 值");

            return buffer.BigEndianToDouble();
        }

        /// <summary>
        /// 从流中读取大端字节并转换为 16 位无符号整数
        /// </summary>
        /// <param name="stream">要读取的流</param>
        /// <returns>转换后的 ushort 值</returns>
        /// <exception cref="ArgumentException">当流不可读时抛出</exception>
        /// <exception cref="EndOfStreamException">当流中没有足够的字节时抛出</exception>
        public static ushort ReadBigEndianUInt16(this Stream stream)
        {
            if (!stream.CanRead)
                throw new ArgumentException("流必须可读", nameof(stream));

            Span<byte> buffer = stackalloc byte[2];
            int bytesRead = stream.Read(buffer);
            if (bytesRead != 2)
                throw new EndOfStreamException("流中没有足够的字节来读取 ushort 值");

            return buffer.BigEndianToUInt16();
        }

        /// <summary>
        /// 从流中读取大端字节并转换为 32 位无符号整数
        /// </summary>
        /// <param name="stream">要读取的流</param>
        /// <returns>转换后的 uint 值</returns>
        /// <exception cref="ArgumentException">当流不可读时抛出</exception>
        /// <exception cref="EndOfStreamException">当流中没有足够的字节时抛出</exception>
        public static uint ReadBigEndianUInt32(this Stream stream)
        {
            if (!stream.CanRead)
                throw new ArgumentException("流必须可读", nameof(stream));

            Span<byte> buffer = stackalloc byte[4];
            int bytesRead = stream.Read(buffer);
            if (bytesRead != 4)
                throw new EndOfStreamException("流中没有足够的字节来读取 uint 值");

            return buffer.BigEndianToUInt32();
        }

        /// <summary>
        /// 从流中读取大端字节并转换为 64 位无符号整数
        /// </summary>
        /// <param name="stream">要读取的流</param>
        /// <returns>转换后的 ulong 值</returns>
        /// <exception cref="ArgumentException">当流不可读时抛出</exception>
        /// <exception cref="EndOfStreamException">当流中没有足够的字节时抛出</exception>
        public static ulong ReadBigEndianUInt64(this Stream stream)
        {
            if (!stream.CanRead)
                throw new ArgumentException("流必须可读", nameof(stream));

            Span<byte> buffer = stackalloc byte[8];
            int bytesRead = stream.Read(buffer);
            if (bytesRead != 8)
                throw new EndOfStreamException("流中没有足够的字节来读取 ulong 值");

            return buffer.BigEndianToUInt64();
        }

        /// <summary>
        /// 将 16 位有符号整数以大端字节序写入流
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 short 值</param>
        /// <exception cref="ArgumentException">当流不可写时抛出</exception>
        public static void WriteBigEndian(this Stream stream, short value)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));

            Span<byte> buffer = stackalloc byte[2];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer);
        }

        /// <summary>
        /// 将 16 位有符号整数以大端字节序写入流，指定字节数
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 short 值</param>
        /// <param name="byteCount">要写入的字节数（1-2）</param>
        /// <exception cref="ArgumentException">当流不可写或 byteCount 无效时抛出</exception>
        public static void WriteBigEndian(this Stream stream, short value, int byteCount)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));
            if (byteCount < 1 || byteCount > 2)
                throw new ArgumentException("byteCount 必须在 1 到 2 之间", nameof(byteCount));

            Span<byte> buffer = stackalloc byte[2];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer.Slice(2 - byteCount, byteCount));
        }

        /// <summary>
        /// 将 32 位有符号整数以大端字节序写入流
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 int 值</param>
        /// <exception cref="ArgumentException">当流不可写时抛出</exception>
        public static void WriteBigEndian(this Stream stream, int value)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));

            Span<byte> buffer = stackalloc byte[4];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer);
        }

        /// <summary>
        /// 将 32 位有符号整数以大端字节序写入流，指定字节数
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 int 值</param>
        /// <param name="byteCount">要写入的字节数（1-4）</param>
        /// <exception cref="ArgumentException">当流不可写或 byteCount 无效时抛出</exception>
        public static void WriteBigEndian(this Stream stream, int value, int byteCount)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));
            if (byteCount < 1 || byteCount > 4)
                throw new ArgumentException("byteCount 必须在 1 到 4 之间", nameof(byteCount));

            Span<byte> buffer = stackalloc byte[4];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer.Slice(4 - byteCount, byteCount));
        }

        /// <summary>
        /// 将 64 位有符号整数以大端字节序写入流
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 long 值</param>
        /// <exception cref="ArgumentException">当流不可写时抛出</exception>
        public static void WriteBigEndian(this Stream stream, long value)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));

            Span<byte> buffer = stackalloc byte[8];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer);
        }

        /// <summary>
        /// 将 64 位有符号整数以大端字节序写入流，指定字节数
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 long 值</param>
        /// <param name="byteCount">要写入的字节数（1-8）</param>
        /// <exception cref="ArgumentException">当流不可写或 byteCount 无效时抛出</exception>
        public static void WriteBigEndian(this Stream stream, long value, int byteCount)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));
            if (byteCount < 1 || byteCount > 8)
                throw new ArgumentException("byteCount 必须在 1 到 8 之间", nameof(byteCount));

            Span<byte> buffer = stackalloc byte[8];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer.Slice(8 - byteCount, byteCount));
        }

        /// <summary>
        /// 将双精度浮点数以大端字节序写入流
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 double 值</param>
        /// <exception cref="ArgumentException">当流不可写时抛出</exception>
        public static void WriteBigEndian(this Stream stream, double value)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));

            Span<byte> buffer = stackalloc byte[8];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer);
        }

        /// <summary>
        /// 将双精度浮点数以大端字节序写入流，指定字节数
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 double 值</param>
        /// <param name="byteCount">要写入的字节数（1-8）</param>
        /// <exception cref="ArgumentException">当流不可写或 byteCount 无效时抛出</exception>
        public static void WriteBigEndian(this Stream stream, double value, int byteCount)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));
            if (byteCount < 1 || byteCount > 8)
                throw new ArgumentException("byteCount 必须在 1 到 8 之间", nameof(byteCount));

            Span<byte> buffer = stackalloc byte[8];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer.Slice(8 - byteCount, byteCount));
        }

        /// <summary>
        /// 将 16 位无符号整数以大端字节序写入流
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 ushort 值</param>
        /// <exception cref="ArgumentException">当流不可写时抛出</exception>
        public static void WriteBigEndian(this Stream stream, ushort value)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));

            Span<byte> buffer = stackalloc byte[2];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer);
        }

        /// <summary>
        /// 将 16 位无符号整数以大端字节序写入流，指定字节数
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 ushort 值</param>
        /// <param name="byteCount">要写入的字节数（1-2）</param>
        /// <exception cref="ArgumentException">当流不可写或 byteCount 无效时抛出</exception>
        public static void WriteBigEndian(this Stream stream, ushort value, int byteCount)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));
            if (byteCount < 1 || byteCount > 2)
                throw new ArgumentException("byteCount 必须在 1 到 2 之间", nameof(byteCount));

            Span<byte> buffer = stackalloc byte[2];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer.Slice(2 - byteCount, byteCount));
        }

        /// <summary>
        /// 将 32 位无符号整数以大端字节序写入流
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 uint 值</param>
        /// <exception cref="ArgumentException">当流不可写时抛出</exception>
        public static void WriteBigEndian(this Stream stream, uint value)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));

            Span<byte> buffer = stackalloc byte[4];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer);
        }

        /// <summary>
        /// 将 32 位无符号整数以大端字节序写入流，指定字节数
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 uint 值</param>
        /// <param name="byteCount">要写入的字节数（1-4）</param>
        /// <exception cref="ArgumentException">当流不可写或 byteCount 无效时抛出</exception>
        public static void WriteBigEndian(this Stream stream, uint value, int byteCount)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));
            if (byteCount < 1 || byteCount > 4)
                throw new ArgumentException("byteCount 必须在 1 到 4 之间", nameof(byteCount));

            Span<byte> buffer = stackalloc byte[4];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer.Slice(4 - byteCount, byteCount));
        }

        /// <summary>
        /// 将 64 位无符号整数以大端字节序写入流
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 ulong 值</param>
        /// <exception cref="ArgumentException">当流不可写时抛出</exception>
        public static void WriteBigEndian(this Stream stream, ulong value)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));

            Span<byte> buffer = stackalloc byte[8];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer);
        }

        /// <summary>
        /// 将 64 位无符号整数以大端字节序写入流，指定字节数
        /// </summary>
        /// <param name="stream">要写入的流</param>
        /// <param name="value">要写入的 ulong 值</param>
        /// <param name="byteCount">要写入的字节数（1-8）</param>
        /// <exception cref="ArgumentException">当流不可写或 byteCount 无效时抛出</exception>
        public static void WriteBigEndian(this Stream stream, ulong value, int byteCount)
        {
            if (!stream.CanWrite)
                throw new ArgumentException("流必须可写", nameof(stream));
            if (byteCount < 1 || byteCount > 8)
                throw new ArgumentException("byteCount 必须在 1 到 8 之间", nameof(byteCount));

            Span<byte> buffer = stackalloc byte[8];
            value.GetBigEndianSpan(buffer);
            stream.Write(buffer.Slice(8 - byteCount, byteCount));
        }
    }
}
